<?php

namespace App\Containers\AppSection\Questionnaire\Traits;

use App\Containers\AppSection\File\Tasks\FindFileByIdTask;
use App\Containers\AppSection\Questionnaire\Models\Field;
use App\Containers\AppSection\Questionnaire\Tasks\FindFieldByIdTask;
use Carbon\Carbon;
use Closure;

trait UpdateQuestionnaireRequestTrait
{
    protected function responsesRule($responses): Closure
    {
        return function ($attribute, $value, $fail) use ($responses) {
            $responseIds = $responses->pluck('id')->toArray();
            $respIds = array_column($value, 'id');

            if (count(array_unique($responseIds)) < count($responseIds)) {
                $fail('Некоторые формы повторяются!');
                return;
            }
            //Отсутствие полей
            $diff = array_values(array_diff($responseIds, $respIds));

            if (count($diff) !== 0) {
                $fail('Некоторые формы отсутствуют!');
            }
            //Лишние поля
            $diff = array_values(array_diff($respIds, $responseIds));

            if (count($diff) !== 0) {
                $fail('Некоторые формы лишние!');
            }
        };
    }

    protected function valuesRule($fields): Closure
    {
        return function ($attribute, $value, $fail) use ($fields) {
            $fieldIds = $fields->pluck('id')->toArray();
            $values = [];
            //Разбиение на лица
            foreach ($value as $v) {
                if (!is_int($v['person'])) {
                    $fail('Поле person имеет нечисловое значение!');
                    return;
                }
                if ($v['person'] >= count($value) / count($fieldIds)) {
                    $fail('Поле person имеет недопустимое значение ' . $v['person'] . '!');
                    return;
                }

                if (!isset($values[$v['person']]))
                    $values[$v['person']] = [];
                $values[$v['person']][] = $v['field_id'];
            }

            //Проверка всех лиц
            foreach ($values as $key => $v) {
                if (count(array_unique($v)) < count($v)) {
                    $fail('У Лица ' . ($key + 1) . ' некоторые поля повторяются!');
                    return;
                }
                //Отсутствие полей
                $diff = array_values(array_diff($fieldIds, $v));

                if (count($diff) !== 0) {
                    $field = app(FindFieldByIdTask::class)->run($diff[0]);
                    if ($field->label)
                        $fail('У Лица ' . ($key + 1) . ' поле "' . $field->label . '" отсутствует');
                    else  $fail('У Лица ' . ($key + 1) . ' некоторые поля отсутствуют');
                    return;
                }
                //Лишние поля
                $diff = array_values(array_diff($v, $fieldIds));
                if (count($diff) !== 0) {
                    $field = app(FindFieldByIdTask::class)->run($diff[0]);
                    if ($field->label)
                        $fail('У Лица ' . ($key + 1) . ' поле "' . $field->label . '" лишнее');
                    else  $fail('У Лица ' . ($key + 1) . ' некоторые поля лишние');
                    return;
                }
            }
        };
    }

    protected function valueRule($values, Field $field, $isDraft = false): Closure
    {
        return function ($attribute, $value, $fail) use ($values, $field, $isDraft) {
            if (empty($field)) {
                $fail('Поле не найдено');
                return;
            }

            if ($field->required || !empty($value['value'][0])) {
                if (count($field->depends_key) !== 0) {
                    $isCheckValid = true;
                    //Зависимости
                    foreach ($field->depends_key as $key => $depend_key) {
                        //Находим ключ
                        $depend = $this->array_search_field_key($depend_key, $values, $value['person']);
                        //Если ключ найден
                        $isCheckValid = $depend !== null && count(array_intersect($depend['value'], $field->depends_value[$key])) !== 0;

                        if (!$isCheckValid) break;
                    }
                    //Если проверка нужна
                    if ($isCheckValid) {
                        if (!isset($value['value'][0]) && !$isDraft) {
                            $fail('Поле пустое');
                            return;
                        }
                        $valid = $this->validateValue($value, $field, $isDraft);
                        if ($valid !== '') {
                            $fail($valid);
                            return;
                        }
                    }
                } else {
                    if (!isset($value['value'][0]) && !$isDraft) {
                        $fail('Поле пустое');
                        return;
                    }
                    $valid = $this->validateValue($value, $field, $isDraft);
                    if ($valid !== '') {
                        $fail($valid);
                        return;
                    }
                }
            }
        };
    }

    protected function array_search_field_key($depend_key, $values, $person): ?array
    {
        foreach ($values as $key => $value) {
            if ($depend_key === $value['field']->key && $value['person'] === $person) {
                return $value;
            }
        }
        return null;
    }

    protected function validateValue($value, $field, $isDraft): string
    {
        if (empty($value['value'])) return '';

        switch ($field->type_key) {
            case 'file':
            {
                if (count($value['value']) > 1)
                    return 'Несколько файлов';

                return $isDraft && !isset($value['value'][0]) ? '' : $this->validateFile($value['value'][0]);
            }
            case 'files':
            {
                foreach ($value['value'] as $v) {
                    if ($isDraft && empty($v)) continue;
                    $valid = $this->validateFile($v);
                    if ($valid !== '') {
                        return $valid;
                    }
                }
                return '';
            }
            case 'date':
            {
                if (count($value['value']) > 1)
                    return 'Несколько значений';
                if ($isDraft && empty($value['value'][0]))
                    return '';
                if (!Carbon::hasFormat($value['value'][0], 'd.m.Y'))
                    return 'Неверная дата';
                return '';
            }
            case 'select':
            {
                if (count($value['value']) > 1)
                    return 'Несколько значений';
                $options = array_column($field->options, 'value');
                if (!in_array($value['value'][0], $options))
                    return 'Неверное значение1';
                return '';
            }
            case 'number':
            {
                if (count($value['value']) > 1)
                    return 'Несколько значений';
                if (!is_numeric($value['value'][0]) && !$isDraft)
                    return 'Неверное значение3';
                return '';
            }
            case 'string':
            case 'text':
            case 'fio':
            case 'address':
            {
                if (count($value['value']) > 1)
                    return 'Несколько значений';
                if (!is_string($value['value'][0]) && !$isDraft)
                    return 'Неверное значение3';
                return '';
            }
            case 'serial_number':
            {
                if (count($value['value']) > 1)
                    return 'Несколько значений';
                if (!preg_match('/^\d{4}-\d{6}$/i', $value['value'][0]) && !$isDraft)
                    return 'Неверное значение4';
                return '';
            }
            case 'inn':
            {
                if (count($value['value']) > 1)
                    return 'Несколько значений';
                if (!preg_match('/^(\d{10}|\d{12})$/i', $value['value'][0]) && !$isDraft)
                    return 'Неверное значение5';
                return '';
            }
        }
        return 'Неверный тип поля';
    }

    private function validateFile($fileId): string
    {
        $file = app(FindFileByIdTask::class)->run($fileId);
        if (!isset($file) || $file->type !== 'questionnaires')
            return 'Неверный файл';
        return '';
    }
}
